package cn.xnatural.jpa;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;

import java.io.Serializable;
import java.security.SecureRandom;

/**
 * nanoid 生产器
 */
public class NanoIDGenerator implements IdentifierGenerator {
    public static final int length = 21;

    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
        return nanoId(length);
    }


    public static final char[] CS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
    protected static final SecureRandom SR = new SecureRandom();


    public static String nanoId() { return nanoId(length, CS); }

    public static String nanoId(int len) { return nanoId(len, CS); }

    /**
     * nano id 生成
     * @param len 生成的长度
     * @param CS 字符集
     */
    public static String nanoId(int len, char[] CS) {
        if (len < 1) throw new IllegalArgumentException("Param len must >= 1");
        if (CS == null || CS.length < 1) throw new IllegalArgumentException("Param CS required");
        final int mask = (2 << (int)Math.floor(Math.log(CS.length - 1) / Math.log(2))) - 1;
        final int step = (int)Math.ceil(1.6 * mask * len / CS.length);
        final StringBuilder sb = new StringBuilder();
        while(true) {
            byte[] bytes = new byte[step];
            SR.nextBytes(bytes);
            for(int i = 0; i < step; ++i) {
                int idx = bytes[i] & mask;
                if (idx < CS.length) {
                    sb.append(CS[idx]);
                    if (sb.length() == len) {
                        return sb.toString();
                    }
                }
            }
        }
    }
}
